home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************\
- *
- * Apple Macintosh Developer Technical Support
- *
- * Source code for the emergency memory routines
- *
- * Program: ColorReset
- * File: EmergMem.c
- *
- * by: Forrest Tanaka
- *
- * Copyright © 1988-1991 Apple Computer, Inc.
- * All rights reserved.
- *
- \******************************************************************************/
-
-
- /******************************************************************************\
- * Header Files
- \******************************************************************************/
-
- #ifndef THINK_C
- #include <OSUtils.h>
- #endif
-
- #include "EmergMem.h"
-
-
- /******************************************************************************\
- * Constants
- \******************************************************************************/
-
- #define kEmergMemSize 32768 /* Number of bytes of emergency mem to allocate */
- #define kMemoryMargin 32768 /* “significant amount” amount of free mem */
-
-
- /******************************************************************************\
- * Global Variables
- \******************************************************************************/
-
- Handle gEmergMem; /* Handle to block of emergency memory */
-
-
- /******************************************************************************\
- * Private: AppGrowZone - Custom grow-zone procedure
- *
- * This is a very basic grow zone procedure. My application keeps a reserve
- * handle of memory in case the Memory Manager gets a request for some memory
- * that is not available in my heap. If memory were to get tight (<32k or so),
- * the Toolbox could crash the system. This grow-zone proc tries to thwart that
- * possibility by releasing the 32K block of emergency memory if it hasn’t been
- * released already and if the amount of memory requested is less than 32K.
- * Hopefully, that’s enough to satisfy the memory request.
- *
- * There are three conditions in which the emergency memory isn’t freed. If the
- * emergency memory is already free, obviously there isn’t much that can be done.
- * If the emergency memory is equal to GZSaveHnd, then it was the reallocation of
- * emergency memory that caused this grow-zone proc to be called. So it doesn’t
- * make much sense to free it in that case. If the size of the memory request is
- * more than the size of emergency memory, then I don’t bother to free emergency
- * memory because I assume that the toolbox handles such huge requests for memory
- * properly. Warning: that isn’t always a good assumption, but that’s not my
- * fault.
- *
- * WARNING: Register A5 might not be valid when grow-zone procedures
- * are called. Read Technical Note #136 and 208.
- *
- * The "cbNeeded" parameter is the number of bytes that the Memory Manager needs
- * to fulfill the memory request it had received. The number of bytes actually
- * freed by AppGrowZone is returned.
- \******************************************************************************/
-
- static pascal long AppGrowZone(
- Size cbNeeded) /* Number of bytes needed by Memory Manager */
- {
- long theA5; /* Value of A5 when AppGrowZone is called */
- long amountFreed; /* Number of bytes freed up */
-
- /* Remember the current value of A5 */
- theA5 = SetCurrentA5();
-
- /* Free emergency memory if possible */
- if (!NoEmergMem() && (gEmergMem != GZSaveHnd()) &&
- (cbNeeded <= kEmergMemSize))
- {
- EmptyHandle( gEmergMem );
- amountFreed = kEmergMemSize;
- }
- else
- amountFreed = 0;
-
- /* Restore A5 */
- (void)SetA5( theA5 );
-
- /* Return number of bytes freed */
- return amountFreed;
- }
-
-
- /******************************************************************************\
- * Public: InstallAppGZ
- *
- * Installing our custom grow-zone procedure simply involves calling SetGrowZone
- * with the address of our custom grow-zone procedure.
- \******************************************************************************/
-
- void InstallAppGZ()
- {
- SetGrowZone( (GrowZoneProcPtr)&AppGrowZone );
- }
-
-
- /******************************************************************************\
- * Public: DeinstallAppGZ
- *
- * Passing NIL to SetGrowZone is all that’s needed to tell the Memory Manager not
- * to call a grow-zone procedure whenenver memory requests can’t be satisfied.
- \******************************************************************************/
-
- void DeinstallAppGZ()
- {
- SetGrowZone( nil );
- }
-
-
- /******************************************************************************\
- * Public: InitEmergMem
- *
- * If the block of emergency memory couldn’t be allocated, then we’re probably in
- * some pretty big trouble. But InitEmergMem tries to deal as best it can in
- * that case by allocating an empty handle; that is, it allocates a master
- * pointer that’s set to NIL. That leaves it up to the rest of the application
- * to decide what to do.
- \******************************************************************************/
-
- void InitEmergMem()
- {
- /* Allocate the block of emergency memory */
- gEmergMem = NewHandle( kEmergMemSize );
- if (gEmergMem == nil)
- /* Couldn’t allocate emergency memory; just allocate an empty handle */
- gEmergMem = NewEmptyHandle();
-
- /* Now that emergency memory is initialized, can install grow zone proc */
- InstallAppGZ();
- }
-
-
- /******************************************************************************\
- * Public: RecoverEmergMem
- *
- * ReallocHandle takes an existing empty handle (handle whose master pointer is
- * nil) and allocates a block of memory for it. Perfect for this job!
- \******************************************************************************/
-
- void RecoverEmergMem()
- {
- ReallocHandle( gEmergMem, kEmergMemSize );
- }
-
-
- /******************************************************************************\
- * Public: FailLowMemory
- *
- * PurgeSpace is used to determine how much free memory there’d be in the heap if
- * all purgeable blocks were purged. If this amount is less than the amount
- * needed, or if there isn’t any emergency memory, true is returned.
- \******************************************************************************/
-
- Boolean FailLowMemory(
- long memRequest) /* Amount of memory to check */
- {
- long total; /* Total amount of free memory if heap was purged */
- long contig; /* Max amount of free contiguous memory if heap was purged */
-
- PurgeSpace( /*<*/&total, /*<*/&contig );
- return (total < (memRequest + kMemoryMargin)) || NoEmergMem();
- }
-
-
- /******************************************************************************\
- * Public: NoEmergMem
- *
- * We check on the handle and the master pointer of gEmergMem to see if the
- * emergency memory block has been emptied by AppGrowZone, or was never allocated
- * in the first place. StripAddress is called because we’re comparing the master
- * pointer of the emergency memory handle against zero, and the upper byte of
- * master pointers can be non-zero if the machine is booted in 24-bit addressing
- * mode.
- \******************************************************************************/
-
- Boolean NoEmergMem()
- {
- /* Empty handle means no emergency memory */
- return (gEmergMem == nil) || (StripAddress( *gEmergMem ) == nil);
- }
-
-
- /******************************************************************************\
- * Public: NewHandleMargin
- *
- * I don’t call SysError with an ID 25 if there isn’t enough memory to satisfy
- * the request, so there isn’t much reason to use the grow-zone proc. So, I
- * deinstall the grow-zone proc temporarily just before I allocate the memory.
- \******************************************************************************/
-
- Handle NewHandleMargin(
- Size requestedSize, /* Number of bytes requested to be allocated */
- Boolean appHeapAlloc, /* Allocate in app heap or system heap? */
- Boolean clearMem) /* Clear allocated memory or leave it alone? */
- {
- Handle aHandle; /* Handle to newly-allocated memory */
-
- if (FailLowMemory( requestedSize ))
- aHandle = nil;
- else
- {
- /* We handle memFullErr properly, so don’t need grow-zone proc */
- DeinstallAppGZ();
-
- /* Allocate the memory with the requested options */
- if (!appHeapAlloc && clearMem)
- aHandle = NewHandleSysClear( requestedSize );
- else if (!appHeapAlloc)
- aHandle = NewHandleSys( requestedSize );
- else if (clearMem)
- aHandle = NewHandleClear( requestedSize );
- else
- aHandle = NewHandle( requestedSize );
-
- /* Install the grow-zone proc again */
- InstallAppGZ();
- }
-
- return aHandle;
- }
-
-
- /******************************************************************************\
- * Public: NewPtrMargin
- *
- * I don’t call SysError with an ID 25 if there isn’t enough memory to satisfy
- * the request, so there isn’t much reason to use the grow-zone proc. So, I
- * disconnect the grow-zone proc temporarily just before I allocate the memory.
- \******************************************************************************/
-
- Ptr NewPtrMargin(
- Size requestedSize, /* Number of bytes requested to be allocated */
- Boolean appHeapAlloc, /* Allocate in app heap or system heap? */
- Boolean clearMem) /* Clear allocated memory or leave it alone? */
- {
- Ptr aPtr; /* Pointer to newly-allocated memory */
-
- if (FailLowMemory( requestedSize ))
- aPtr = nil;
- else
- {
- /* We handle memFullErr properly, so don’t need grow-zone proc */
- DeinstallAppGZ();
-
- /* Allocate the memory with the requested options */
- if (!appHeapAlloc && clearMem)
- aPtr = NewPtrSysClear( requestedSize );
- else if (! appHeapAlloc)
- aPtr = NewPtrSys( requestedSize );
- else if (clearMem)
- aPtr = NewPtrClear( requestedSize );
- else
- aPtr = NewPtr( requestedSize );
-
- /* Connect up the grow-zone proc again */
- InstallAppGZ();
- }
-
- return aPtr;
- }
-